package org.jeecg.modules.yc.NB.Services.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.ArrayUtils;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.mapper.SysDepartMapper;
import org.jeecg.modules.yc.NB.Model.*;
import org.jeecg.modules.yc.NB.Services.NBCommandService;
import org.jeecg.modules.yc.NB.Services.NBNotificationService;
import org.jeecg.modules.yc.NB.protocol.YCAnalysis;
import org.jeecg.modules.yc.util.TransformationTool;
import org.jeecg.modules.yc.web.entity.*;
import org.jeecg.modules.yc.web.mapper.DeviceReportMapper;
import org.jeecg.modules.yc.web.mapper.TerminalMapper;
import org.jeecg.modules.yc.web.mapper.PdDeviceMapper;
import org.jeecg.modules.yc.web.service.ITerminalReportService;
import org.jeecg.modules.yc.web.service.IYcAlarmReportService;
import org.jeecg.modules.yc.web.service.IYcCableJointService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service
public class NBNotificationServiceImpl implements NBNotificationService {

    private final static String nbPrefix = "NB_";
    private final static ReentrantLock terminalLock = new ReentrantLock();
    private final static ReentrantLock DeviceLock = new ReentrantLock();

    private final static List<String> idsList = Arrays.asList("1831180909214724097","1849405985470922754","1849405988092362753","1849677773622853633");
    private final static List<String> deviceNos = Collections.singletonList("1861243821970354177");

    @Autowired
    private TerminalMapper terminalMapper;
    @Autowired
    private NBCommandService nbCommandService;
    @Autowired
    private SysDepartMapper sysDepartMapper;
    @Autowired
    private PdDeviceMapper ycPdDeviceMapper;
    @Autowired
    private DeviceReportMapper deviceReportMapper;
    @Autowired
    private ITerminalReportService terminalReportService;
    @Autowired
    private IYcAlarmReportService ycAlarmReportService;
    @Autowired
    private IYcCableJointService ycCableJointService;

    @Override
    public void createOrDelete(AddDeleteDevice addDeleteDevice) {
        List<SysDepart> departList = this.sysDepartMapper.queryDepartsByUsername(nbPrefix+addDeleteDevice.getTenantId());
        if(ObjectUtils.isEmpty(departList)){
            return;
        }
        String operation = addDeleteDevice.getOperation();
        List<Device> deviceList = addDeleteDevice.getDeviceList();
        if("add".equals(operation)){
            for(Device device : deviceList){
                Terminal terminal = Terminal.builder()
                        .createBy(nbPrefix+addDeleteDevice.getTenantId())
                        .name(device.getDeviceName())
                        .no(device.getDeviceId())
                        .imei(device.getImei())
                        .productId(addDeleteDevice.getProductId())
                        .sysOrgCode(departList.get(0).getOrgCode())
                        .tenantId(addDeleteDevice.getTenantId())
                        .build();
                terminalMapper.insert(terminal);
            }

        }else if("delete".equals(operation)){
            for(Device device : deviceList){
                QueryWrapper queryWrapper =  new QueryWrapper<>();
                queryWrapper.eq("no",device.getDeviceId());
                terminalMapper.delete(queryWrapper);
            }
        }
    }

    @Override
    public void dataReport(DataReport dataReport) {
        if(ArrayUtils.isEmpty(dataReport.getPayloadAPPdata())){
            return ;
        }
        System.out.println("收到数据________："+new Date()+TransformationTool.bytesHexString(dataReport.getPayloadAPPdata()));
        YCAnalysisModel model = YCAnalysis.analysis(dataReport.getPayloadAPPdata());
        if(model == null){
            return;
        }
        Terminal terminal = terminalMapper.selectByDeviceId(dataReport.getDeviceId());
        if(terminal == null){
            //如果数据库没有汇集单元则自动增加
            /*terminal = addTerminal(dataReport.getDeviceId(),dataReport.getTenantId());
            if(ObjectUtils.isEmpty(terminal)){
                return;
            }*/
            return;
        }
        byte[] hexData = model.getDataArea();
        //判断数据类型，终端数据a88a,传感器数据a66a
        if(model.getHead() == (byte)0xA8){
            TerminalReport terminalReport;
            switch (model.getFnc()){
                case 0x13: //改变采集器上的温度和湿度，并存储上传记录
                    int temperature = ((hexData[0] & 0xFF) << 8 | (hexData[1] & 0xFF))/100;
                    int humidity    = ((hexData[2] & 0xFF) << 8 | (hexData[3] & 0xFF))/100;
                    terminalMapper.updateByDeviceId(dataReport.getDeviceId(),String.valueOf(temperature),String.valueOf(humidity));
                    terminalReport = new TerminalReport();
                    terminalReport.setTerminalId(terminal.getId());
                    terminalReport.setTerminalNo(terminal.getNo());
                    terminalReport.setTerminalName(terminal.getName());
                    terminalReport.setSysOrgCode(terminal.getSysOrgCode());
                    terminalReport.setHumidity(String.valueOf(humidity/100));
                    terminalReport.setTemperature(String.valueOf(temperature/100));
                    terminalReportService.save(terminalReport);
                    break;
                case 0x14://汇集单元上报入网传感器ID
                    break;
                case 0x15://电压采集数据上传
                    Terminal param = new Terminal();
                    param.setNo(dataReport.getDeviceId());
                    param.setVoltage( String.valueOf((float) ((hexData[0] & 0xFF) << 8 | (hexData[1] & 0xFF)) /100));
                    param.setTemperature(String.valueOf(((hexData[2] & 0xFF) << 8 | (hexData[3] & 0xFF))/100));
                    param.setHumidity(String.valueOf(((hexData[4] & 0xFF) << 8 | (hexData[5] & 0xFF))/100));
                    terminalMapper.updateByNo(param);
                    terminalReport = new TerminalReport();
                    terminalReport.setTerminalId(terminal.getId());
                    terminalReport.setTerminalNo(terminal.getNo());
                    terminalReport.setTerminalName(terminal.getName());
                    terminalReport.setSysOrgCode(terminal.getSysOrgCode());
                    terminalReport.setVoltage( String.valueOf((float) ((hexData[0] & 0xFF) << 8 | (hexData[1] & 0xFF)) /100));
                    terminalReport.setTemperature(String.valueOf(((hexData[2] & 0xFF) << 8 | (hexData[3] & 0xFF))/100));
                    terminalReport.setHumidity(String.valueOf(((hexData[4] & 0xFF) << 8 | (hexData[5] & 0xFF))/100));
                    terminalReportService.save(terminalReport);
                    break;
            }
        }else if(model.getHead() == (byte)0xA6){
            switch (model.getFnc()){
                case 0x01:
                    String no = Hex.encodeHexString(model.getDeviceId());//传感器编号
                    PdDevice ycPdDevice = ycPdDeviceMapper.queryByDeviceIdAndNo(terminal.getId(),no);
                    if(ycPdDevice == null){
                        return;
                        //局放传感器不存在添加进去
                        // ycPdDevice = addPdDevice(dataReport,no ,terminal);
                    }
                    // 解析局放数据
                    int minTemp = Integer.MAX_VALUE;
                    int maxTemp = Integer.MIN_VALUE;
                    Integer minDBNonNull = null; // 用于存储不为0的最小湿度值
                    int maxDB = Integer.MIN_VALUE;
                    // 用于记录每个湿度值出现的次数
                    Map<Integer, Integer> humidityCount = new HashMap<>();
                    // 每两个字节表示一个温度数据，紧接着的两个字节表示一个湿度数据
                    for (int i = 0; i < hexData.length - 4; i += 4) {
                        // 检查是否还有足够的字节来读取温度和湿度
                        if (i + 3 < hexData.length) {
                            // 提取温度数据（两个字节）
                            int temp = (hexData[i] & 0xFF) << 8 | (hexData[i + 1] & 0xFF);
                            // 提取湿度数据（两个字节）
                            int humidity = (hexData[i + 2] & 0xFF) << 8 | (hexData[i + 3] & 0xFF);
                            // 更新温度的最大和最小值
                            minTemp = Math.min(minTemp, temp);
                            maxTemp = Math.max(maxTemp, temp);
                            // 更新湿度的最大和最小值
                            if (humidity != 0) {
                                if (minDBNonNull == null || humidity < minDBNonNull) {
                                    minDBNonNull = humidity;
                                }
                            }
                            maxDB = Math.max(maxDB, humidity);
                            // 更新局放出现的次数
                            humidityCount.put(humidity, humidityCount.getOrDefault(humidity, 0) + 1);
                        }
                    }
                    int minDB = (minDBNonNull != null) ? minDBNonNull : 0;
                    // 找出出现次数最多的湿度值
                    int mostFrequentDB = 0;
                    int maxCount = 0;
                    for (Map.Entry<Integer, Integer> entry : humidityCount.entrySet()) {
                        if (entry.getValue() > maxCount) {
                            maxCount = entry.getValue();
                            mostFrequentDB = entry.getKey();
                        }
                    }


                    String voltage = String.valueOf((float)((hexData[hexData.length - 4] & 0xFF) << 8 | (hexData[hexData.length - 3] & 0xFF))/1000);
                    int reference = (hexData[hexData.length - 2] & 0xFF) << 8 | (hexData[hexData.length - 1] & 0xFF);


                    String maxDBStr,minDBStr,mostFrequentDBStr;
                    double alarmValue;
                  /*  if(deviceNos.contains(ycPdDevice.getId())){
                        alarmValue = DBMath1(maxDB,reference);
                        maxDBStr = String.format("%.2f",alarmValue );
                        minDBStr = String.format("%.2f", DBMath1(minDB,reference));
                        mostFrequentDBStr =String.format("%.2f", DBMath1(mostFrequentDB,reference));
                    }else {*/
                    alarmValue = DBMath(maxDB,reference);
                    maxDBStr = String.format("%.2f",alarmValue );
                    minDBStr =  String.format("%.2f", DBMath(minDB,reference));
                    mostFrequentDBStr = String.format("%.2f", DBMath(mostFrequentDB,reference));
                    /* }*/
                    if(alarmValue > 30){
                        YcCableJoint ycCableJoint = ycCableJointService.getById(ycPdDevice.getCableJointId());
                        YcAlarmReport ycAlarmReport = new YcAlarmReport();
                        if(ycCableJoint != null ){
                            ycAlarmReport.setLineId(ycCableJoint.getLineId());
                        }
                        ycAlarmReport.setDeviceId(ycPdDevice.getId());
                        ycAlarmReport.setDeviceNo(ycPdDevice.getNo());
                        ycAlarmReport.setTerminalId(terminal.getId());
                        ycAlarmReport.setTerminalNo(terminal.getNo());
                        ycAlarmReport.setSysOrgCode(terminal.getSysOrgCode());
                        ycAlarmReport.setType("0");
                        ycAlarmReport.setAlarmMsg("数据终端： "+ terminal.getName() + "  传感器： "+ycPdDevice.getName()+" 检测到局放值为 "+maxDBStr+" 当前值过大");
                        ycAlarmReportService.save(ycAlarmReport);
                    }
                    //将上报的温度湿度数据写入
                    ycPdDeviceMapper.updateById(PdDevice.builder()
                            .id(ycPdDevice.getId())
                            .communicationTime(new Date())
                            .temperature(String.valueOf(maxTemp/10))
                            .pdStrength(maxDBStr)

                            .voltage(voltage)
                            .build());
                 /*   // 创建一个Date对象（例如，当前时间）

                    Date now = new Date();
                    // 使用Calendar来操作Date对象
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(now); // 设置Calendar的时间为Date对象的时间
                    // 给Calendar对象加20分钟
                    calendar.add(Calendar.MINUTE, 20*model.getFrameContinuation());
                    // 获取修改后的Date对象
                    Date newDate = calendar.getTime();*/
                    //上报数据加入数据库

                    deviceReportMapper.insert(DeviceReport.builder().deviceNo(no).deviceName(ycPdDevice.getName())
                            .pdMax(maxDBStr)
                            .pdMin(minDBStr)
                            .createTime(new Date())
                            .terminalName(terminal.getName())
                            .terminalNo(dataReport.getDeviceId())
                            .terminalId(terminal.getId())
                            .temperatureMax(String.valueOf(maxTemp/10))
                            .temperatureMin(String.valueOf(minTemp/10))
                            .metadata(TransformationTool.bytesHexString(dataReport.getPayloadAPPdata()))
                            .voltage(voltage)
                            .sysOrgCode(ycPdDevice.getSysOrgCode())
                            .mostFrequentDb(mostFrequentDBStr)
                            .build());
                    break;
                case (byte) 0x99:
                    break;
            }

        }

    }
    public static double generateRandomNumberBetween18And22() {

        // 生成一个 0.0 到 4.0 之间的随机数，然后缩放到 18.0 到 22.0 之间

        double randomValue = 18.0 + Math.random() * 4.0;



        // 使用 DecimalFormat 来四舍五入到两位小数

        DecimalFormat df = new DecimalFormat("#.00");

        double roundedValue = Double.parseDouble(df.format(randomValue));



        // 为了确保有机会得到 22.00，我们可以检查随机数是否非常接近 22.0 但小于它，

        // 如果是这样，我们就将其设置为 22.00。但请注意，这会影响随机分布。

        // 一个更简单的方法是接受 22.00 不作为严格上限。

        if (roundedValue >= 21.995 && roundedValue < 22.0) {

            roundedValue = 22.00;

        }



        return roundedValue;

    }

    @Override
    public void upAndDown(UpAndDown upAndDown) {
        QueryWrapper<Terminal> queryWrapper =  new QueryWrapper<>();
        queryWrapper.eq("no",upAndDown.getDeviceId());
        Terminal terminal = terminalMapper.selectOne(queryWrapper);
        if(ObjectUtils.isEmpty(terminal)){//有设备更改设备在线状态和最后一次在线时间
            return;
            /*try {
                addTerminal(upAndDown.getDeviceId(),upAndDown.getTenantId());
                return;
            } catch (Exception e) {
                e.printStackTrace();
            }*/
        }
        //设备存在
        terminal.setLastConnectTime(new Date());
        terminal.setCommunicationStatus(upAndDown.getEventType());
        terminalMapper.updateById(terminal);
    }

    private synchronized Terminal addTerminal(String deviceId,String tenantId) {
        terminalLock.lock();
        Terminal terminal = terminalMapper.selectByDeviceId(deviceId);
        if(terminal != null ){
            return terminal;
        }
        List<SysDepart> departList = getDeptOrgCodeByNBTenantId(tenantId);
        if(ObjectUtils.isEmpty(departList)){
            return null;
        }
        try {
            Device device = nbCommandService.queryDevice(deviceId);
            terminal = Terminal.builder()
                    .createBy(nbPrefix+tenantId)
                    .name(device.getDeviceName())
                    .no(device.getDeviceId())
                    .imei(device.getImei())
                    .productId(device.getProductId())
                    .sysOrgCode(departList.get(0).getOrgCode())
                    .tenantId(device.getTenantId())
                    .lastConnectTime(new Date())
                    .createTime(new Date(Long.parseLong(device.getCreateTime())))
                    .communicationStatus("1")
                    .build();
            terminalMapper.insert(terminal);
            return terminal;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            terminalLock.unlock();
        }
    }

    private synchronized PdDevice addPdDevice(DataReport dataReport , String no,Terminal terminal){
        DeviceLock.lock();
        PdDevice ycPdDevice = ycPdDeviceMapper.queryByDeviceIdAndNo(terminal.getId(),no);
        if(ycPdDevice != null){
            return ycPdDevice;
        }
        ycPdDevice = PdDevice.builder()
                .no(no).terminalId(terminal.getId())
                .sysOrgCode(terminal.getSysOrgCode())
                .createBy(nbPrefix+dataReport.getTenantId())
                .name(terminal.getName()+"_"+no)
                .communicationTime(new Date())
                .createTime(new Date()).build();
        ycPdDeviceMapper.insert(ycPdDevice);
        DeviceLock.unlock();
        return ycPdDevice;
    }

    private List<SysDepart> getDeptOrgCodeByNBTenantId(String tenantId){
        return this.sysDepartMapper.queryDepartsByUsername(nbPrefix+tenantId);
    }

    private double DBMath(int db,int reference){
        if(db <= reference ){
            return 0;
        }
        float dbMath = ((float) db)-((float)reference);
        return 20*Math.log10(dbMath);
    }
  /*  private double DBMath1(int db,int reference){
        if(db <= reference ){
            return 0;
        }
        float dbMath = ((float) db)-((float)reference);
        return 10*Math.log10(dbMath*13);
    }*/

}
